package com.yangrd.helper.crudhelper.core.crud.page;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yangrd.helper.crudhelper.common.util.IOUtils;
import com.yangrd.helper.crudhelper.common.util.ReflectionUtils;
import com.yangrd.helper.crudhelper.core.EmbeddedSelect;
import com.yangrd.helper.crudhelper.core.support.CtrlUrlSupport;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.EnumUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;

import java.io.IOException;
import java.lang.reflect.Field;
import java.time.temporal.Temporal;
import java.util.*;

/**
 * description: AmisCurdTemplate
 *
 * @author: yangrd
 * @date: 2020/9/14
 * @version: 1.0
 */
@RequiredArgsConstructor
public class AmisCurdTemplate {

    @Value("${crud-helper.context-path:/admin}")
    private String contextPath;

    private final TranslateService translateService;

    private Set<String> notModify = new HashSet<>();

    private Set<String> notCols = new HashSet<>();

    {
        notCols.add("serialVersionUID");
        notCols.add("id");
        notCols.add("params");
        notModify.add("createdTime");
        notModify.add("lastModifiedTime");
    }

    public String genCrudTemplate(Class<?> clazz) {

        String value = contextPath + "/" + DomainMappingManager.MappingSupport.getDomainName(clazz);


        List<Field> fields = ReflectionUtils.listAllField(clazz);
        JSONObject res = getJsonObject("template.json");
        Assert.notNull(res, "不能为空");
        JSONArray body = res.getJSONArray("body");
        JSONObject addObj = body.getJSONObject(0);
        JSONObject addObj1 = addObj.getJSONObject("drawer").getJSONObject("body");
        addObj1.put("api", "post:" + value);

        JSONArray addControls = addObj1.getJSONArray("controls");

        JSONObject curdBody = body.getJSONObject(1);
        curdBody.put("api", value);
        JSONArray filterControls = curdBody.getJSONObject("filter").getJSONArray("controls");
        JSONArray columns = curdBody.getJSONArray("columns");


        JSONObject modifiedCol = getJsonObject("modified.json");
        JSONObject modifiedFrom = modifiedCol.getJSONArray("buttons").getJSONObject(0);
        JSONObject drawer = modifiedFrom.getJSONObject("drawer").getJSONObject("body");
        drawer.put("initApi", value + "/${id}");
        drawer.put("api", "put:" + value + "/${id}?waitSeconds=1");
        JSONArray modifiedControls = drawer.getJSONArray("controls");
        JSONObject deleteAction = curdBody.getJSONArray("bulkActions").getJSONObject(0);
        deleteAction.put("api", "delete:" + value + "?ids=${ids|raw}");
        fields.forEach(field -> {
            build(addControls, filterControls, columns, modifiedControls, field);
        });

        columns.add(modifiedCol);
        return res.toJSONString();
    }

    private void build(JSONArray addControls, JSONArray filterControls, JSONArray columns, JSONArray modifiedControls, Field field) {
        String name = field.getName();
        String label = translateService.translateCN(name);
        if (!notCols.contains(name)) {
            JSONObject filterControl = new JSONObject();
            String type = "text";
            if (field.getType().equals(String.class)) {
                type = "text";
            } else if (field.getType().equals(Date.class) || Temporal.class.isAssignableFrom(field.getType())) {
                type = "date-range";
                filterControl.put("format", "YYYY-MM-DD");
            } else if (Number.class.isAssignableFrom(field.getType())) {
                type = "number";
            } else if (field.getType().equals(Boolean.class)) {
                type = "select";
                JSONArray options = new JSONArray();
                JSONObject option1 = new JSONObject();
                option1.put("label", "是");
                option1.put("value", "true");
                options.add(option1);
                JSONObject option2 = new JSONObject();
                option2.put("label", "否");
                option2.put("value", "false");
                options.add(option2);
                filterControl.put("options", options);
            } else if (Enum.class.isAssignableFrom(field.getType())) {
                type = "select";
                List<Enum> enumList = EnumUtils.getEnumList((Class<Enum>) field.getType());
                filterControl.put("clearable", true);
                JSONArray options = new JSONArray();
                enumList.forEach(anEnum -> {
                    String value = anEnum.name();
                    Object n = value;
                    if (ReflectionUtils.hasMethods(field.getType(), "getName")) {
                        n = ReflectionUtils.getMethodReturnVal(field.getType(), "getName", anEnum);
                    }
                    JSONObject option = new JSONObject();
                    option.put("label", n);
                    option.put("value", value);
                    options.add(option);
                });
                filterControl.put("options", options);
            } else if (field.isAnnotationPresent(EmbeddedSelect.class)) {
                EmbeddedSelect annotation = field.getAnnotation(EmbeddedSelect.class);
                type = "select";
                filterControl.put("clearable", true);
                String url = annotation.url();
                if (Strings.isBlank(annotation.url())) {
                    Class<?> urlDomainClass = annotation.value();
                    url = "get:" + CtrlUrlSupport.getBaseCtrlUrl(urlDomainClass) + "/options";
                }

                filterControl.put("source", url);
            }

            filterControl.put("label", label);
            filterControl.put("type", type);
            if (field.isAnnotationPresent(EmbeddedSelect.class)) {
                filterControl.put("name", name + "$id");
            } else {
                filterControl.put("name", name);
            }

            filterControl.put("placeholder", "请输入" + label);
            filterControls.add(filterControl);

            JSONObject col = new JSONObject();
            if (field.isAnnotationPresent(EmbeddedSelect.class)) {
                col.put("name", "params." + name + ".name");
            } else {
                col.put("name", name);
            }
            col.put("label", label);
            col.put("sortable", true);
            col.put("searchable", true);
            columns.add(col);

            if (!notModify.contains(name)) {
                addControls.add(buildControl(name, label, field));

                modifiedControls.add(buildControl(name, label, field));
            }


        }
    }


    private JSONObject buildControl(String name, String label, Field field) {
        JSONObject control = new JSONObject();

        String type = null;
        if (field.getType().equals(String.class)) {
            type = "text";
        } else if (field.getType().equals(Date.class) || Temporal.class.isAssignableFrom(field.getType())) {
            type = "date-range";
            control.put("format", "YYYY-MM-DD");
        } else if (Number.class.isAssignableFrom(field.getType())) {
            type = "number";
        } else if (field.getType().equals(Boolean.class)) {
            type = "select";
            control.put("clearable", true);
            JSONArray options = new JSONArray();
            JSONObject option1 = new JSONObject();
            option1.put("label", "是");
            option1.put("value", "true");
            options.add(option1);
            JSONObject option2 = new JSONObject();
            option2.put("label", "否");
            option2.put("value", "false");
            options.add(option2);
            control.put("options", options);
        } else if (Enum.class.isAssignableFrom(field.getType())) {
            type = "select";
            control.put("clearable", true);
            List<Enum> enumList = EnumUtils.getEnumList((Class<Enum>) field.getType());
            JSONArray options = new JSONArray();
            enumList.forEach(anEnum -> {
                String value = anEnum.name();
                Object n = value;
                if (ReflectionUtils.hasMethods(field.getType(), "getName")) {
                    n = ReflectionUtils.getMethodReturnVal(field.getType(), "getName", anEnum);
                }
                JSONObject option = new JSONObject();
                option.put("label", n);
                option.put("value", value);
                options.add(option);
            });
            control.put("options", options);
        } else if (field.isAnnotationPresent(EmbeddedSelect.class)) {
            EmbeddedSelect annotation = field.getAnnotation(EmbeddedSelect.class);
            type = "select";
            control.put("clearable", true);
            String url = annotation.url();
            if (Strings.isBlank(annotation.url())) {
                Class<?> urlDomainClass = annotation.value();
                url = "get:" + CtrlUrlSupport.getBaseCtrlUrl(urlDomainClass) + "/options";
            }
            if (Collection.class.isAssignableFrom(field.getType())) {
                control.put("multiple", true);
                name = "params." + name + ".ids";
            } else {
                name = "params." + name + ".id";
            }
            control.put("source", url);
        }
        control.put("name", name);
        control.put("label", label);
        control.put("type", type);
        return control;
    }


    private JSONObject getJsonObject(String filename) {
        ClassPathResource classPathResource = new ClassPathResource(filename);
        try {
            return JSON.parseObject(IOUtils.inputStream2String(classPathResource.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


}
